package com.tianji.learning.service.impl;

import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.api.dto.leanring.LearningRecordDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningRecordFormDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.SectionType;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.ILearningRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.utils.LearningRecordDelayTaskHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * <p>
 * 学习记录表 服务实现类
 * </p>
 *
 * @author llt
 * @since 2024-07-11
 */
@Service
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {

    private final ILearningLessonService lessonService;

    private final CourseClient courseClient;

    private final LearningRecordDelayTaskHandler taskHandler;

    @Override
    public LearningLessonDTO queryLearningRecordByCourse(Long courseId) {
        // 1. 获取当前用户id
        Long userid = UserContext.getUser();

        // 2. 通过 courseId 和 userId 查询课表信息
        LearningLesson lesson = lessonService.lambdaQuery()
                .eq(LearningLesson::getUserId, userid)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        if (lesson == null) {
            throw new BizIllegalException("该课程未加入课表");
        }

        // 3. 通过 lessonId 和 userId 查询学习记录
        List<LearningRecord> recordList = lambdaQuery()
                .eq(LearningRecord::getLessonId, lesson.getId())
                .eq(LearningRecord::getUserId, userid)
                .list();

        // 4. 封装返回结果
        LearningLessonDTO dto = new LearningLessonDTO();
        dto.setId(lesson.getId());
        dto.setLatestSectionId(lesson.getLatestSectionId());
        List<LearningRecordDTO> dtoList = BeanUtils.copyList(recordList, LearningRecordDTO.class);
        dto.setRecords(dtoList);

        return dto;
    }

    @Override
    public void addLearningRecord(LearningRecordFormDTO dto) {
        // 1. 获取当前登陆用户 id
        Long userId = UserContext.getUser();

        // 2. 处理学习记录
        boolean isFinished = false; // 本小节是否第一次已学完
        if (dto.getSectionType().equals(SectionType.VIDEO)) {
            // 2.1 提交视频播放
            isFinished = handleVideoRecord(userId, dto);
        } else {
            // 2.2 提交考试记录
            isFinished = handleExamRecord(userId, dto);
        }

        // 3. 处理课表数据
        if (!isFinished) {
            // 本小节不是第一次学完 不用处理课表数据
            return;
        }
        handleLessonData(dto);
    }

    // 处理课表数据
    private void handleLessonData(LearningRecordFormDTO dto) {
        // 1. 查询课表 learning_lesson
        LearningLesson lesson = lessonService.getById(dto.getLessonId());
        if (lesson == null) {
            throw new BizIllegalException("课表不存在");
        }

        // 2. 判断是否是第一次学完
        boolean allFinished = false;

        // 3. 远程调用课程服务 得到课程信息
        CourseFullInfoDTO course = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        if (course == null) {
            throw new BizIllegalException("课表不存在");
        }
        Integer sectionNum = course.getSectionNum();

        // 4. 学完的话, 判断该课程全部课程是否学完
        Integer learnedSections = lesson.getLearnedSections();
        allFinished = learnedSections + 1 >= sectionNum;

        // 5. 更新数据
        lessonService.lambdaUpdate()
                .set(lesson.getStatus() == LessonStatus.NOT_BEGIN, LearningLesson::getStatus, LessonStatus.LEARNING)
                .set(LearningLesson::getLatestSectionId, dto.getSectionId())
                .set(LearningLesson::getLatestLearnTime, dto.getCommitTime())
                .set(allFinished, LearningLesson::getStatus, LessonStatus.FINISHED)
                .setSql("learned_sections = learned_sections + 1")
                .eq(LearningLesson::getId, lesson.getId())
                .update();
    }

    // 处理提交视频记录
    private boolean handleVideoRecord(Long userId, LearningRecordFormDTO dto) {
        // 1. 查询旧的学习记录
        LearningRecord learningRecord = queryOldRecord(dto.getLessonId(), dto.getSectionId());

        // 2. 判断是否存在
        boolean isFinished = false; // isFinished 为 true 代表第一次学完
        if (learningRecord == null) {
            // 3. 不存在新增
            // 3.1 将 dto 转为 po
            LearningRecord po = BeanUtils.copyBean(dto, LearningRecord.class);
            po.setUserId(userId);

            // 3.2 保存到数据库
            boolean save = save(po);
            if (!save) {
                throw new DbException("新增视频记录失败");
            }
        } else {
            // 4. 存在更新
            isFinished = !learningRecord.getFinished() && dto.getMoment() * 2 >= dto.getDuration();
            // 如果没有学完存入缓存
            if (!isFinished) {
                // 存入到缓存
                LearningRecord record = new LearningRecord();
                record.setLessonId(dto.getLessonId());
                record.setSectionId(dto.getSectionId());
                record.setMoment(dto.getMoment());
                record.setFinished(learningRecord.getFinished());
                record.setId(learningRecord.getId());// 学习记录 id
                taskHandler.addLearningRecordTask(record);
                return false;
            }
            boolean update = lambdaUpdate()
                    .set(LearningRecord::getMoment, dto.getMoment())
                    .set(isFinished, LearningRecord::getFinished, true)
                    .set(isFinished, LearningRecord::getFinishTime, dto.getCommitTime())
                    .eq(LearningRecord::getId, learningRecord.getId())
                    .update();
            if (!update) {
                throw new DbException("更新学习记录失败");
            }
            // 清理 redis 缓存
            taskHandler.cleanRecordCache(dto.getLessonId(), dto.getSectionId());
        }

        return true;
    }

    private LearningRecord queryOldRecord(Long lessonId, Long sectionId) {
        // 1. 查询缓存
        LearningRecord cache = taskHandler.readRecordCache(lessonId, sectionId);

        // 2. 查到的话返回
        if (cache != null) {
            return cache;
        }

        // 3. 没有的话查询数据库
        LearningRecord dbRecord = lambdaQuery()
                .eq(LearningRecord::getLessonId, lessonId)
                .eq(LearningRecord::getSectionId, sectionId)
                .one();
        // 如果数据库中没有的话返回null 防止在存入缓存的时候报空指针
        if (dbRecord == null) {
            return null;
        }

        // 4. 存入缓存
        taskHandler.writeRecordCache(dbRecord);
        return dbRecord;
    }

    // 处理提交考试记录
    private boolean handleExamRecord(Long userId, LearningRecordFormDTO dto) {
        // 1. 将 dto 转为 po
        LearningRecord po = BeanUtils.copyBean(dto, LearningRecord.class);
        po.setUserId(userId);
        po.setFinished(true);
        po.setFinishTime(dto.getCommitTime());

        // 2. 保存到数据库
        boolean save = save(po);
        if (!save) {
            throw new DbException("新增考试记录失败");
        }

        return true;
    }
}
